Skip to content

Conversation

@jonathan-buttner
Copy link
Contributor

This PR refactors a base test class for testing service classes (e.g. OpenAiService).

Highlights

  • OpenAiServiceTests now leverages the base class
  • The parsePersistedConfig from OpenAI were moved to AbstractInferenceServiceParameterizedTests so they can be leveraged for all classes that extend from it
  • Created AbstractInferenceServiceParameterizedTests which uses parameterized tests to represent the parsePersistedConfig and parsePersistedConfigWithSecrets tests they can be removed from each subclassed tests
  • Moved parsePersistedConfigWithSecrets tests from the AbstractInferenceServiceTests to AbstractInferenceServiceParameterizedTests
  • Fixed a bug in the CustomService where we were parsing the chunking settings using the old default settings instead of the new ones

@jonathan-buttner jonathan-buttner added >refactoring :ml Machine learning Team:ML Meta label for the ML team v9.2.0 labels Sep 25, 2025
var chunkingSettings = extractChunkingSettings(config, taskType);
ChunkingSettings chunkingSettings = null;
if (TaskType.TEXT_EMBEDDING.equals(taskType)) {
chunkingSettings = ChunkingSettingsBuilder.fromMap(removeFromMapOrDefaultEmpty(config, ModelConfigurations.CHUNKING_SETTINGS));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the bug fix. extractChunkingSettings was using removeFromMap which if the settings don't exist it would provide null to the ChunkingSettingsBuilder.fromMap(). We intentionally do this when parsing from persistent state to handle backwards compatibility I think.

I don't think the change here in parseRequestConfig will cause any backwards compatibility issues. For new endpoints being created we'll use the newer default chunking settings instead though.

// To find this information you need to access your account's limits https://platform.openai.com/account/limits
// 500 requests per minute
private static final RateLimitSettings DEFAULT_RATE_LIMIT_SETTINGS = new RateLimitSettings(500);
public static final RateLimitSettings DEFAULT_RATE_LIMIT_SETTINGS = new RateLimitSettings(500);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making various things public so they can be accessible in tests that are outside of the package.

);
}

public static String parsePersistedConfigErrorMsg(String inferenceEntityId, String serviceName, TaskType taskType) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a new version of the error message to make the error more clear. Ideally all of the services will switch to use this one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to switch the services to use the new method in this PR? There are a dozen tests that would need to be updated to reflect the new message, but it would be nice to have consistency across all services.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep I can do that 👍

}

@Override
protected void assertRerankerWindowSize(RerankingInferenceService rerankingInferenceService) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this to the configuration of the tests so that it can be leveraged by CustomServiceTests and CustomServiceParameterizedTests.

}

@Override
public InferenceService createInferenceService() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was pushed into the abstract base class

@@ -0,0 +1,387 @@
/*
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason this class exists and isn't included in AbstractInferenceServiceTests is adding parameterized tests in AbstractInferenceServiceTests results in the the non parameterized tests being run for each permutation, even though those tests don't depend on the parameterized test information.

It seemed cleaner to separate them 🤷‍♂️

{
new TestCaseBuilder(
"Test parsing persisted config without chunking settings",
testConfiguration -> getPersistedConfigMap(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test configurations have to be passed in because the parameters() method must be static

*/
public abstract class AbstractInferenceServiceTests extends InferenceServiceTestCase {

protected final MockWebServer webServer = new MockWebServer();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulled this logic into the base class

protected abstract Map<String, Object> createTaskSettingsMap();

protected abstract Map<String, Object> createSecretSettingsMap();
public void testParseRequestConfig_CreatesAnEmbeddingsModel() throws Exception {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The *ParseRequestConfig_* tests could be converted into parameterized tests as well. I decided to not do it in this PR because it's already quite a few changes.

protected abstract Map<String, Object> createTaskSettingsMap();

protected abstract Map<String, Object> createSecretSettingsMap();
public void testParseRequestConfig_CreatesAnEmbeddingsModel() throws Exception {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm adding a few tests that were in OpenAiServiceTests to make them common.

@jonathan-buttner jonathan-buttner marked this pull request as ready for review September 25, 2025 19:26
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/ml-core (Team:ML)

);
}

public static String parsePersistedConfigErrorMsg(String inferenceEntityId, String serviceName, TaskType taskType) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to switch the services to use the new method in this PR? There are a dozen tests that would need to be updated to reflect the new message, but it would be nice to have consistency across all services.


public static String parsePersistedConfigErrorMsg(String inferenceEntityId, String serviceName, TaskType taskType) {
return format(
"Failed to parse stored model [%s] for [%s] service, error: [%s]. Please delete and add the service again",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would deleting and adding the service again actually help if the task type was unsupported?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think deleting is probably the only solution here. I think this would only occur if the the inference endpoint got corrupted somehow. Basically this is saying that the persisted inference endpoint is stating it is leverage a particular task type that is not supported. The request context parsing should prevent getting into that scenario. But if we had a regression or the endpoint was corrupted somehow we could.

serviceSettingsMap,
secretSettingsMap,
parsePersistedConfigErrorMsg(modelId, NAME)
parsePersistedConfigErrorMsg(modelId, NAME, taskType)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To reduce some code duplication and prevent us from constructing a String that we might never use every time we call parseRequestConfig(), I think it should be possible to move the parsePersistedConfigErrorMsg() call down into createModel() along with the unsupportedTaskTypeErrorMsg() call used for the REQUEST context and only call them (which one we call would depend on the context passed in to createModel()) when throwing an exception that would need the message.

I haven't checked every Service to see if this would work for all of them, but I assume that the structure is pretty similar between implementations.

Comment on lines -160 to -166
private static ChunkingSettings extractChunkingSettings(Map<String, Object> config, TaskType taskType) {
if (TaskType.TEXT_EMBEDDING.equals(taskType)) {
return ChunkingSettingsBuilder.fromMap(removeFromMap(config, ModelConfigurations.CHUNKING_SETTINGS));
}

return null;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to keep using this method in the two places below, where the behaviour is unchanged, rather than duplicating the logic?


private static ChunkingSettings extractPersistentChunkingSettings(Map<String, Object> config, TaskType taskType) {
if (TaskType.TEXT_EMBEDDING.equals(taskType)) {
// note there's
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfinished comment

@jonathan-buttner jonathan-buttner enabled auto-merge (squash) September 30, 2025 15:30
@jonathan-buttner jonathan-buttner merged commit 698a7b6 into elastic:main Sep 30, 2025
34 checks passed
@jonathan-buttner jonathan-buttner deleted the ml-refactor-tests-abstract branch September 30, 2025 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:ml Machine learning >refactoring Team:ML Meta label for the ML team v9.2.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants